package pers.cyril.jdb.protocol.codec;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import org.reflections.Reflections;
import pers.cyril.jdb.protocol.packet.annotation.Command;
import pers.cyril.jdb.protocol.packet.Packet;
import pers.cyril.jdb.protocol.serializer.Serializer;
import pers.cyril.jdb.protocol.serializer.SerializerAlgorithm;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 文件描述
 *
 * @author yichao wang
 */
public class PacketCodeC {

    public static final PacketCodeC INSTANCE = new PacketCodeC();

    public static final int MAGIC_NUMBER = 0x12345678;

    private static final Map<Byte, Class<? extends Packet>> requestTypeMap = new HashMap<>();

    static {
        Reflections reflections = new Reflections(Packet.class.getPackage().getName());

        Set<Class<?>> packetSet = reflections.getTypesAnnotatedWith(Command.class);
        for (Class<?> clazz : packetSet) {
            if (Packet.class.isAssignableFrom(clazz)) {
                Command command = clazz.getAnnotation(Command.class);
                requestTypeMap.put(command.value(), (Class<? extends Packet>) clazz);
            }
        }
    }

    public void encode(ByteBuf byteBuf, Packet packet) {
        Command command = packet.getClass().getAnnotation(Command.class);
        if (command == null) {
            throw new IllegalArgumentException(packet.getClass().getName() + " must have @Command annotation");
        }
        byte[] bytes = Serializer.DEFAULT.serialize(packet);
        byteBuf.writeInt(MAGIC_NUMBER);
        byteBuf.writeByte(packet.getVersion());
        byteBuf.writeByte(Serializer.DEFAULT.getSerializerAlgorithm());
        byteBuf.writeByte(command.value());
        byteBuf.writeInt(bytes.length);
        byteBuf.writeBytes(bytes);
    }

    public ByteBuf encode(ByteBufAllocator allocator, Packet packet) {
        return encode(allocator, packet, Serializer.DEFAULT);
    }

    public ByteBuf encode(ByteBufAllocator allocator, Packet packet, Serializer serializer) {
        Command command = packet.getClass().getAnnotation(Command.class);
        if (command == null) {
            throw new IllegalArgumentException(packet.getClass().getName() + " must have @Command annotation");
        }
        ByteBuf byteBuf = allocator.buffer();
        byte[] bytes = serializer.serialize(packet);
        byteBuf.writeInt(MAGIC_NUMBER);
        byteBuf.writeByte(packet.getVersion());
        byteBuf.writeByte(Serializer.DEFAULT.getSerializerAlgorithm());
        byteBuf.writeByte(command.value());
        byteBuf.writeInt(bytes.length);
        byteBuf.writeBytes(bytes);
        return byteBuf;
    }

    public Packet decode(ByteBuf byteBuf) {
        //跳过 magic number
        byteBuf.skipBytes(4);
        // 跳过版本号
        byteBuf.skipBytes(1);
        // 序列化算法标识
        byte serializeAlgorithm = byteBuf.readByte();
        // 指令
        byte command = byteBuf.readByte();

        // 数据包长度
        int length = byteBuf.readInt();

        byte[] bytes = new byte[length];
        byteBuf.readBytes(bytes);

        Class<? extends Packet> requestType = getRequestType(command);

        Serializer serializer = getSerializer(serializeAlgorithm);

        if (requestType != null && serializer != null) {
            return serializer.deserialize(requestType, bytes);
        }
        return null;
    }

    private Serializer getSerializer(byte serializeAlgorithm) {
        if (serializeAlgorithm == SerializerAlgorithm.JSON) {
            return Serializer.DEFAULT;
        }
        return null;
    }

    private Class<? extends Packet> getRequestType(byte command) {
        return requestTypeMap.get(command);
    }
}
